home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
TPUG - Toronto PET Users Group
/
TPUG Users Group CD
/
TPUG Users Group CD.iso
/
PET
/
S-Super PET
/
(s)t2.d64
/
RESAMPLE.FTN
< prev
next >
Wrap
Text File
|
2009-01-18
|
17KB
|
491 lines
* R E S A M P L E . F T N
* ***********************
* NOTE: REMOVE COMMENTS BEFORE RUNNING. DATA ARRAYS NEED THE SPACE.
* For purposes of time series analysis, the number of data points
* must be an exact power of 2, at least when Fast Fourier Transform,
* and related filter algorithms are used. However, a time series
* will generally not consist of exactly the correct number of data
* points, particularly if the data has been obtained through tests
* or experimentation. It is common to simply truncate the data file
* or pad zeros to the end in order to obtain the required number of
* points. Neither of these methods is entirely satisfactory.
* The present program accepts a time series data file as input and
* creates from it a new file containing any number of points
* desired, including powers of 2. When an increased number of
* points is requested, direct spline interpolation is used to obtain
* the new data points which will lie between the old data points.
* When a smaller number of data points is requested, The time series
* is first passed through a low pass filter before resampling with
* the spline interpolation. The filter cut-off frequency is set at
* less than half the new sample frequency to avoid any aliasing
* difficulties.
* The program begins by requesting the name of the source data file.
* It is assumed that the source file will have been formatted by the
* program 'dataform.ftn' or 'datout'. The four header records of
* this file will be read and displayed to show the number of data
* points, the time origin and the sample interval. A request will
* then be made for the number of data points desired and the
* destination file name. The new sample interval and cutoff
* frequency will be displayed and the processing will proceed. The
* new sample interval dt' is calculated from the old sample interval'
* dt using the relation:
* dt' = dt * ( n - 1 ) / ( n' - 1 )
* where n is the number of data points in the source file and n' is'
* the number of points in the new file. The cutoff frequency fc
* for the original data file is calculated by assuming it is equal
* to half the sample frequency. Thus:
* fc = (1/2) * 1 / dt
* and this is expressed in cycles per second if dt is measured in
* seconds. if n' is less than n, a low pass filter is applied'
* before interpolation. The new cutoff frequency will then be:
* fc' = (1/2) * 1 / dt'
* When this is expressed in the usual normalized form with the
* sample interval taken as unity, and the frequency expressed in
* radians per sample interval then:
* omega' = fc' * pi * dt
* = (1/2) * pi * ( n' - 1 ) / ( n - 1)'
* P R O G R A M S T R U C T U R E
* *********************************
* The program roughly conforms to the following steps:
* Request name of source data file
* open Source data file
* read source data file header
* Display header information
* read source data file into data array
* close source file
* Request destination file information
* Check that n' .gt. 2 and n' .ne. n
* if n' .LT. n THEN'
* Calculate new file cutoff frequency and filter length
* Display destination file header information
* Extend data array at both ends
* Apply lowpass filter to data array
* else
* Display destination file information
* endif
* Save the filtered data in a temporary file
* execute subroutine to calculate spline sigma array
* Re-read the filtered data from the temporary file
* open output file
* write output file header records
* Initialize data record counter
* while record counter .lt. next to last record
* Calculate interpolated data one record at a time
* write output record
* endwhile
* Pad last record with zeros if necessary
* write last Record
* close output file
* end
* L O W P A S S F I L T E R
* ****************************
* This subroutine replaces the series x of length n by a
* smoothed version obtained with a low-pass digital filter. The
* parameter m is the maximum length of x , that is, the dimension
* assigned to x in the calling program. The filter length is
* (2 * ns + 1) terms and the filter weights represent the least
* squares approximation to the cutoff filter with cutoff frequency
* w . The cutoff frequency w is expressed in radians per sample.
* The filter coefficients are proportional to:
* sin( i * w ) sin( 2 * pi * i / ( 2 * ns + 1) )
* ----------- * --------------------------------
* i * w 2 * pi * i / ( 2 * ns + 1 )
* where the first term is simply the Fourier Transform of the
* rectangular pass band with cutoff frequency w , and the second
* term is a convergence factor to limit the Gibb's oscillations.'
* The coefficients are normalized so that their sum is unity,
* providing unity gain for a constant input array, that is, at zero
* frequency. The transfer function for this filter will have a
* transition band of frequency width given by 4 * pi / (2 * ns + 1)
* which suggests that a large value of ns would reduce this
* transition band width.
* to avoid loss of data due to the finite length of the filter, the
* input array is extended symmetrically at both ends by ns
* positions. This approach is not perfect since it will introduce
* some phase distortion in the filtered results at both ends. The
* extended array occupies n + 2 * ns positions and the maximum
* size allowed for x must be at least as large as the extended
* array. The filtered result is left in the lower n positions.
* The size of ns is a balance between the desire for a small
* frequency transition band and a desire to limit the distortion
* length at the array ends as well as to accelerate the
* computations. A cost may be defined consisting of the sum of the
* proportion of the frequency transition band to the pass band, and
* the proportion of the distortion length to the array length. An
* "optimum" value of ns may then be obtained by minimizing this
* cost. Thus:
* 4 * pi / ( 2 * ns + 1 ) 2 * ns
* cost = A * ----------------------- + B * ------
* w n
* where A and B are weights which may be used to adjust the
* importance of each term. The minimization results in the filter
* half length of:
* ns = ( SQRT( (A / B) * 4 * pi * n / w ) - 1 ) / 2
* and the smallest integer part of this expression is taken.
* The particular filter chosen here is a two sided, symmetric,
* non-recursive filter. This ensures that phase distortion and
* delays will not be introduced, except at the end points. The
* filtering could be accomplished more rapidly if a single sided
* non-recursive filter were employed since FFT methods could then be
* used. However, the time delays introduced this way could make it
* difficult to relate the filtered data to real time. Consequently
* the two sided filter has been chosen.
* S P L I N E I N T E R P O L A T I O N
* ****************************************
* The spline interpolation subroutine is based on the program
* published in the book "Computer Methods for Mathematical"
* Computations" by Forsythe, Malcolm and Moler, Prentice-Hall 1977,"
* Page 76. The program has been modified to reduce storage
* requirements by capitalizing on the fact that the data is
* available at equally spaced intervals. Further more, rather than
* calculating and storing the three cubic coefficients for each data
* point, only the so called 'sigma' array is stored. That is, the
* interpolated points are obtained from the following interpolation
* equation:
* x(z) = w * x(i+1) + w' * x(i)'
* + h*h * [w*(w*w-1)*s(i+1) + w'*(w'*w'-1)*s(i)]'
* where
* h = sample interval
* w = z - h * i
* w' = 1 - w'
* s(i) = i'th sigma quantity'
* ************************************************************************
real x(1700), s(1700), tzero, dt, t(5)
character source$file, destination$file, file$id, var$fmt, garbag
* DIMENSIONS OF x AND s AND THE PARAMETER m DETERMINE MAX ARRAY SIZE.
m = 1700
pi = 4.0 * atan(1.0)
* CLEAR SCREEN, IDENTIFY PROGRAM AND REQUEST DATA FILE NAME.
write(6,"'1'")
print," P R O G R A M R E S A M P L E . F T N"
print," ****************************************"
print
print," Enter the source file name"
read, source$file
* READ THE INPUT DATA FILE.
call data$input(source$file, file$id, m, var$fmt, tzero, dt, fc, x, n)
* HOW MANY POINTS IN RE-SAMPLED FILE? ENSURE NUMBER IS IN ACCEPTABLE RANGE.
loop
write(6,1) char(11),char(11)
1 format(1x,2a1,"Enter number of data points for the destination file")
read, nprime
quitif (nprime .gt. 2) .and. (nprime .ne. n)
write(6,"'+ '")
endloop
* ENTER AND DISPLAY OUTPUT FILE HEADER INFORMATION.
* APPLY LOW PASS FILTER IF RESAMPLING AT A REDUCED RATE.
print,"Enter destination file name"
read, destination$file
file$id = file$id//"- RESAMPLED"
dtprime = dt * float(n - 1) / float(nprime - 1)
print
print,"DESTINATION FILE PARAMETERS"
if nprime .lt. n
fcprime = 3600.0 * 0.5 / dtprime
call display(file$id, nprime, var$fmt, tzero, dtprime, fcprime)
w = 0.5 * pi * float(nprime-1) / float(n-1)
call lowpass(x, n, m, w)
else
fcprime = fc
call display(file$id, nprime, var$fmt, tzero, dtprime, fcprime)
endif
* CALCULATE SPLINE INTERPOLATION ARRAY FOR EQUALLY SPACED DATA BUT FIRST
* SAVE THE FILTERED x ARRAY IN A TEMPORARY FILE SINCE splineq WILL
* DESTROY IT. RE-READ THE DATA WHEN splineq IS FINISHED.
open(unit=8,file="temporary.dat")
write(8,var$fmt) (x(i),i=1,n)
close(unit=8)
call splineq(n, m, x, s)
open(unit=8,file="temporary.dat")
read(8,var$fmt) (x(i),i=1,n)
close(unit=8)
* WRITE THE FOUR OUTPUT FILE HEADER RECORDS. USE THE STANDARD FORMATS.
open(unit=8,file=destination$file)
write(8,"a79,'1'") file$id
write(8,"i5,74x,'2'") nprime
write(8,"a79,'3'") var$fmt
write(8,"3f10.5,49x,'4'") tzero, dtprime, fcprime
* OBTAIN INTERPOLATED POINTS AND WRITE TO FILE, RECORD BY RECORD.
l = 4
factor = float(n-1)/float(nprime-1)
jend = nprime/5
do j = 1 , jend
k = 5 * (j - 1) - 1
do i = 1 , 5
u = 1.0 + float(k+i) * factor
t(i) = val(n, u, x, s)
enddo
l = j + 4
write(8,"5e15.8,i5") (t(i),i=1,5), l
enddo
* PAD LAST RECORD WITH ZEROS IF NEEDED.
k = 5 * jend
if k .lt. nprime
k = k - 1
do i = 1 , 5
t(i) = 0.0
u = 1.0 + float(k+i) * factor
if (k+i) .lt. nprime
t(i) = val(n, u, x, s)
endif
enddo
l = l + 1
write(8,"5e15.8,i5") (t(i),i=1,5), l
endif
close(unit=8)
end
subroutine data$input(file$name, file$id, m, var$fmt, tzero, dt, fc, x, n)
character file$name, file$id, var$fmt, two$characters, two$blanks
integer n, character$count
real tzero, dt, fc, x(m)
open(unit=8,file=file$name)
* READ FILE ID AND REMOVE TRAILING BLANKS.
read(8,"a72") file$id
character$count = 0
two$blanks = " "
while character$count .lt. 72
character$count = character$count + 1
two$characters = substr(file$id, character$count, 2)
quitif two$characters = two$blanks
endwhile
file$id = substr(file$id, 1, character$count)
* READ REMAINING THREE HEADER RECORDS.
read(8,"i5") n
read(8,"a9") var$fmt
read(8,"3f10.5") tzero, dt, fc
* CALCULATE ASSUMED FILE CUTOFF FREQUENCY IF NOT RECORDED.
if fc = 0.0
fc = 3600.0 * 0.5 / dt
endif
* DISPLAY THE HEADER INFORMATION.
call display(file$id, n, var$fmt, tzero, dt, fc)
* READ THE DATA.
read(8,var$fmt) (x(j),j=1,n)
close(unit=8)
return
end
subroutine display(id,n,vform,t,dt,f)
character id,vform
print
print," File Identifier: ", id
print,"Number of Data Points: ", n
print," Data Format: ", vform
print," Time Origin: ", t
print," Time Step: ", dt
write(6,1) f
1 format(" Cutoff frequency: ", f6.2, " Cycles per Hour"///)
return
end
subroutine lowpass(x, n, m, w)
real x(m), h(100)
integer hsize
hsize = 100
pi = 4.0 * atan(1.0)
* CALCULATE FILTER HALF LENGTH.
A = 1.0
B = 10.0
ns = ( sqrt( (A / B) * 4 * pi * float(n) / w ) -1 ) / 2.0
length = 2 * ns + 1
if length .gt. hsize
print,"Filter length =",length," exceeds dimension specification"
stop
endif
if (n + 2 * ns) .gt. m
print,"Extended x array exceeds available dimension"
stop
endif
* EXTEND THE ARRAY.
n1 = n+ns+1
n2 = n+1
do i = 1 , n
x(n1-i) = x(n2-i)
enddo
n1 = ns+1
n2 = n+ns
do i = 1 , ns
x(n1-i) = x(n1+i)
x(n2+i) = x(n2-i)
enddo
* CALCULATE FILTER WEIGHTS.
hzero = w / pi
con = 2.0 * pi / float(2*ns+1)
sum = hzero
do i = 1 , ns
h(i) = hzero * sinc( float(i) * w ) * sinc( float(i) * con )
sum = sum + 2.0 * h(i)
enddo
hzero = hzero / sum
do i = 1 , ns
h(i) = h(i) / sum
enddo
* APPLY FILTER WEIGHTS.
do i = 1 , n
ins = i + ns
temp = hzero * x(ins)
do j = 1 , ns
temp = temp + (x(ins+j) + x(ins-j)) * h(j)
enddo
x(i) = temp
enddo
return
end
function sinc(z)
sinc = sin(z) / z
end
subroutine splineq(n, m, x, s)
real x(m), s(m)
* NOTE: x ARRAY IS DESTROYED BY THIS ROUTINE.
if (n .le. 3)
print," Array length is less than or equal to 3. This"
print," spline routine needs at least 4 points."
print," Execution has been terminated."
stop
endif
* SETUP TRIDIAGONAL SYSTEM FOR EQUALLY SPACED INTERVALS.
nm1 = n - 1
s(2) = x(2)-x(1)
bi = 4.0
do i = 2 , nm1
s(i+1) = x(i+1) - x(i)
s(i) = s(i+1) - s(i)
enddo
* END CONDITIONS.
b1 = -1.0
bn = -1.0
s(1) = ( s(3) - s(2) ) / 2.0
s(n) = ( s(n-1) - s(n-2) ) / 2.0
s(1) = s(1) / 3.0
s(n) = -s(n) / 3.0
* FORWARD ELIMINATION. THIS IS WHERE THE x ARRAY IS USED FOR STORAGE
* OF THE DIAGONAL b ELEMENTS.
b = b1
x(1) = b
do i = 2 , n
t = 1.0/b
b = bi - t
x(i) = b
s(i) = s(i)-t*s(i-1)
enddo
bn = bn - t
* BACK SUBSTITUTION.
s(n) = s(n)/bn
b = 1.0/t
do j = 1 , nm1
i = n-j
b = x(i)
s(i) = (s(i)-s(i+1))/b
enddo
return
end
real function val(n, u, x, s)
real x(n), s(n), u
integer i
i = u
if (i .ge. n) i = n - 1
w = u - float(i)
wbar = 1.0 - w
temp = w * x(i+1) + wbar * x(i)
temp = temp + w*(w+1.0)*(w-1.0)*s(i+1)
temp = temp + wbar*(wbar+1.0)*(wbar-1.0)*s(i)
val = temp
return
end